package com.cloudkinto.service.zone.impl;

import cn.afterturn.easypoi.excel.entity.ExportParams;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloudkinto.common.constant.SysConstant;
import com.cloudkinto.common.exception.BizException;
import com.cloudkinto.common.utils.ExcelUtils;
import com.cloudkinto.common.utils.StringsUtil;
import com.cloudkinto.common.utils.TimeUtils;
import com.cloudkinto.common.utils.ali.AliOss;
import com.cloudkinto.common.web.PageResult;
import com.cloudkinto.common.web.SingleResult;
import com.cloudkinto.dao.UserDao;
import com.cloudkinto.dao.ZoneTempDao;
import com.cloudkinto.entity.UserDo;
import com.cloudkinto.entity.ZoneTempDo;
import com.cloudkinto.entity.ZoneTempRuleDo;
import com.cloudkinto.service.operate.OperateRecordService;
import com.cloudkinto.vo.ft.FreightStatusUpdateReq;
import com.cloudkinto.vo.sc.ErrorInfo;
import com.cloudkinto.service.zone.ZoneTempRuleService;
import com.cloudkinto.service.zone.ZoneTempService;
import com.cloudkinto.vo.zone.*;
import com.cloudkinto.vo.zone.importVo.ZoneTempErrorExportVo;
import com.cloudkinto.vo.zone.importVo.ZoneTempImportVo;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 分区规则 service实现类
 * </p>
 *
 * @author hua
 * @since 2024-11-01
 */
@Service
@Transactional(rollbackFor = Exception.class)//事务回滚
public class ZoneTempServiceImpl extends ServiceImpl<ZoneTempDao, ZoneTempDo> implements ZoneTempService {
    @Autowired
    private ZoneTempDao dao;
    @Autowired
    private ZoneTempRuleService zoneTempRuleService;
    @Autowired
    private UserDao userDao;
    @Value("${gbc.uploadPath.filePathTemp}")
    private String filePathTemp;
    @Value("${gbc.uploadPath.filePath}")
    private String filePath;
    @Value("${gbc.uploadPath.fileUrl}")
    private String fileUrl;
    @Autowired
    private OperateRecordService operateRecordService;

    @Override
    public SingleResult addInit() {
        return SingleResult.success("");
    }


    public void check(ZoneTempAddReq addReq, Long id, Long tenantId) {
        if (StringUtils.isBlank(addReq.getZoneTempName())) throw new BizException(SysConstant.ZoneTemp_Add_Name_Empty);
        //名称不能重复
        ZoneTempDo et = dao.selectOne(new LambdaQueryWrapper<ZoneTempDo>()
                .ne(id != null, ZoneTempDo::getId, id)
                .eq(ZoneTempDo::getZoneTempName, addReq.getZoneTempName())
                .eq(ZoneTempDo::getTenantId, tenantId));
        if (et != null) throw new BizException(SysConstant.ZoneTemp_Add_Name_Same);

        for (ZoneTempRuleAddReq rule : addReq.getList()) {
            String s = this.checkPostCode(rule.getStartPostCode(), rule.getEndPostCode());
            if (StringUtils.isNotBlank(s)) throw new BizException(s);
        }
    }

    @Override
    public String checkPostCode(String startPostCode, String endPostCode) {
        if (StringUtils.isBlank(startPostCode) || StringUtils.isBlank(endPostCode))
            return SysConstant.ZoneTemp_Add_PostCode_Empty;
        if (startPostCode.length() != endPostCode.length()) return SysConstant.PostCode_Length_Error;
        PostCodeRes postCode = this.getPostCode(startPostCode, endPostCode, this.getZimuMap());
        if (postCode.getBegin() > postCode.getEnd()) return (SysConstant.Post_Start_Bigger);
        return null;
    }

    @Override
    public Object add(ZoneTempAddReq req, Long userId, Long tenantId) {
        this.check(req, null, tenantId);
        ZoneTempDo entityDo = new ZoneTempDo();
        BeanUtils.copyProperties(req, entityDo);
        entityDo.setCreateBy(userId);
        entityDo.setTenantId(tenantId);
        dao.insert(entityDo);
        zoneTempRuleService.update(req.getList(), entityDo.getId(), "zoneTemp", userId);
        return entityDo.getId();
    }

    @Override
    public SingleResult updateInit(Long id) {
        return SingleResult.success("");
    }

    @Override
    public Object update(ZoneTempUpdateReq req, Long userId, Long tenantId) {
        this.check(req, req.getId(), tenantId);
        ZoneTempDo entityDo = dao.selectById(req.getId());
        BeanUtils.copyProperties(req, entityDo);
        entityDo.setUpdateBy(userId);
        dao.updateById(entityDo);

        zoneTempRuleService.update(req.getList(), entityDo.getId(), "zoneTemp", userId);

        return entityDo.getId();
    }

    @Override
    public int delete(Long id, Long userId, Long tenantId) {
        int i = dao.updateDelete(id, userId, tenantId);
        return i;
    }

    @Override
    public ZoneTempDetailRes detail(Long id) {
        ZoneTempDo entityDo = dao.selectById(id);
        ZoneTempDetailRes res = new ZoneTempDetailRes();
        BeanUtils.copyProperties(entityDo, res);
        //获取分区规则列表
        List<ZoneTempRuleDo> list = zoneTempRuleService.list(new LambdaQueryWrapper<ZoneTempRuleDo>().eq(ZoneTempRuleDo::getTempId, id)
                .eq(ZoneTempRuleDo::getZoneType, "zoneTemp"));
        List<ZoneTempRuleAddReq> rules = new ArrayList<>();
        for (ZoneTempRuleDo e : list) {
            ZoneTempRuleAddReq req = new ZoneTempRuleAddReq();
            BeanUtils.copyProperties(e, req);
            rules.add(req);
        }
        res.setList(rules);

        return res;
    }


    @Override
    public SingleResult pageInit() {
        return SingleResult.success("");
    }

    @Override
    public List<ZoneTempPageRes> getList(Map<String, String> map) {
        QueryWrapper<ZoneTempDo> wrapper = queryBuild(map);
        List<ZoneTempDo> list = this.list(wrapper);
        List<ZoneTempPageRes> result = handlerListAfter(list);
        return result;
    }

    @Override
    public PageResult getListPage(Map<String, String> map) {
        QueryWrapper<ZoneTempDo> wrapper = queryBuild(map);
        IPage<ZoneTempDo> page = pageInit(map);
        page = this.page(page, wrapper);
        List<ZoneTempPageRes> resList = handlerListAfter(page.getRecords());
        PageResult result = new PageResult(resList, (int) page.getCurrent(), (int) page.getSize(), (int) page.getTotal(), (int) page.getPages());
        return result;
    }

    //查询分页列表之后对列表数据的处理
    private List<ZoneTempPageRes> handlerListAfter(List<ZoneTempDo> list) {
        Map<Long, String> userNameMap = new HashMap<>();
        List<ZoneTempPageRes> result = new ArrayList<>();
        //分区数量
        Map<Long, List<ZoneTempRuleDo>> zoneTemp = null;
        if (!list.isEmpty()) {
            zoneTemp = zoneTempRuleService.list(new LambdaQueryWrapper<ZoneTempRuleDo>().eq(ZoneTempRuleDo::getZoneType, "zoneTemp")
                    .in(ZoneTempRuleDo::getTempId, list.stream().map(ZoneTempDo::getId).collect(Collectors.toList()))).stream().collect(Collectors.groupingBy(ZoneTempRuleDo::getTempId));
        }
        for (ZoneTempDo et : list) {
            ZoneTempPageRes res = new ZoneTempPageRes();
            BeanUtils.copyProperties(et, res);
            if (et.getUpdateBy() != null && userNameMap.get(et.getUpdateBy()) == null) {
                UserDo userDo = userDao.selectById(et.getUpdateBy());
                if (userDo != null) userNameMap.put(et.getUpdateBy(), userDo.getUserAccount());
            }
            res.setUpdateByName(userNameMap.get(et.getUpdateBy()));
            if (zoneTemp.get(et.getId()) != null) res.setZoneNumber(zoneTemp.get(et.getId()).size());
            result.add(res);
        }
        return result;
    }

    private IPage<ZoneTempDo> pageInit(Map<String, String> map) {
        IPage<ZoneTempDo> page = new Page();
        page.setSize(StringUtils.isNotBlank(map.get("pageSize")) ? Long.parseLong(map.get("pageSize")) : 10);
        page.setCurrent(StringUtils.isNotBlank(map.get("current")) ? Long.parseLong(map.get("current")) : 1);
        return page;
    }

    private QueryWrapper<ZoneTempDo> queryBuild(Map<String, String> map) {
        QueryWrapper<ZoneTempDo> wrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(map.get("zoneTempName"))) {
            wrapper.like("zone_temp_name", map.get("zoneTempName"));
        }
        if (StringUtils.isNotBlank(map.get("tenantId"))) {
            wrapper.eq("tenant_id", map.get("tenantId"));
        }
        wrapper.orderByDesc("update_time");
        return wrapper;
    }

    @Override
    public Map<String, String> getZimuMap() {
        List<String> list = Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
                "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z");
        Map<String, String> map = new HashMap<>();
        for (int i = 0; i < list.size(); i++) {
            StringBuilder builder = new StringBuilder();
            builder.append(i + 1);
            if (builder.length() < 10) {
                builder.insert(0, 1);
            }
            map.put(list.get(i), builder.toString());
        }
        return map;
    }


    /**
     * 校验分区
     * @param thirdId
     * @param zoneType freight 物流费
     * @return
     */
    @Override
    public ZoneTempTestRes testZone(Long thirdId, String countryCode, String postcode, String zoneType) {

        //获取物流费分区
        Map<String, String> map = new HashMap<>();
        map.put("zoneType", "freight");
        map.put("id", thirdId + "");
        map.put("countryCode", countryCode);
        map.put("postcode",postcode);
        return this.testZone(map);
    }

    @Override
    public ZoneTempTestRes testZone(Map<String, String> map) {
        ZoneTempTestRes res = new ZoneTempTestRes();
        Map<String, String> zimuMap = this.getZimuMap();
        String postCode = map.get("postcode");
        //将其转成数字
        String[] post = this.getPostArr(postCode, zimuMap);
        List<ZoneTempRuleAddReq> tempTestInfos = new ArrayList<>();
        List<ZoneTempRuleDo> list = zoneTempRuleService.list(new LambdaQueryWrapper<ZoneTempRuleDo>().eq(ZoneTempRuleDo::getTempId, map.get("id"))
                .eq(ZoneTempRuleDo::getZoneType, map.get("zoneType"))
                .eq(ZoneTempRuleDo::getCountryCode, map.get("countryCode")).eq(StringUtils.isNotBlank(map.get("city")), ZoneTempRuleDo::getCity, map.get("city")));
        for (ZoneTempRuleDo et : list) {
            if (StringUtils.isBlank(postCode)) {
                //添加
                tempTestInfos.add(new ZoneTempRuleAddReq(et.getZone(), et.getCountryCode(), et.getCity(), et.getStartPostCode(), et.getEndPostCode()));
                continue;
            }
            //转换邮编
            boolean flag = this.jugPostCode(post, et.getStartPostCode(), et.getEndPostCode(), zimuMap);

            if (flag) {
                tempTestInfos.add(new ZoneTempRuleAddReq(et.getZone(), et.getCountryCode(), et.getCity(), et.getStartPostCode(), et.getEndPostCode()));
            }
        }
        res.setZoneList(tempTestInfos);
        return res;
    }

    public String[] getPostArr(String postCode, Map<String, String> zimuMap) {
        postCode = "1" + postCode;
        postCode = postCode.replaceAll("[^0-9A-Za-z]", "");
        String[] post = new String[0];
        //将其转成数字
        if (StringUtils.isNotBlank(postCode)) {
            post = new String[postCode.length()];
            for (int i = 0; i < postCode.length(); i++) {
                String s = String.valueOf(postCode.charAt(i));
                if ("-".equals(s)) {
                    continue;
                }
                String postCodeIntValue = this.getPostCodeIntValue(s, zimuMap);
                post[i] = postCodeIntValue;
            }
        }
        return post;
    }

    @Override
    public boolean jugPostCode(String postCode, String startPostCode, String endPostCode) {
        Map<String, String> zimuMap = this.getZimuMap();
        //将其转成数字
        String[] post = this.getPostArr(postCode, zimuMap);
        return this.jugPostCode(post, startPostCode, endPostCode, zimuMap);
    }

    public boolean jugPostCode(String[] post, String startPostCode, String endPostCode, Map<String, String> zimuMap) {
        if (post == null || post.length == 0) return false;
        if (zimuMap == null) zimuMap = this.getZimuMap();
        //转换邮编
        PostCodeRes postRes = this.getPostCode(startPostCode, endPostCode, zimuMap);
        String[][] arr = postRes.getArr();
        //组成数字
        StringBuilder targetCode = new StringBuilder();
        //将postCode 替换
        for (int i = 0; i < post.length; i++) {
            if (i == postRes.getTong()) {
                break;
            }
            if ("-1".equals(post[i])) {
                if (i < arr.length) {
                    targetCode.append(arr[i][0]);
                } else {
                    targetCode.append("9");
                }
            } else {
                targetCode.append(post[i]);
            }
        }


        return Long.parseLong(targetCode.toString()) >= postRes.getBegin() && Long.parseLong(targetCode.toString()) <= postRes.getEnd();
    }

    @Override
    public PostCodeRes getPostCode(String startPost, String endPost, Map<String, String> zimuMap) {
        startPost = "1"+startPost;
        endPost = "1"+endPost;
        int tong = -1;
        String[][] arr = new String[startPost.length()][2];
        for (int i = 0; i < startPost.length(); i++) {
            String s = String.valueOf(startPost.charAt(i));
            String e = String.valueOf(endPost.charAt(i));
            arr[i][0] = this.getPostCodeIntValue(s, zimuMap);
            arr[i][1] = this.getPostCodeIntValue(e, zimuMap);
        }
        //组成数字
        StringBuilder begin = new StringBuilder();
        StringBuilder end = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            if (arr[i][0].equals("-1")) {
                tong = i;
                break;
            }
            begin.append(arr[i][0]);
            end.append(arr[i][1]);
        }
        return new PostCodeRes(tong, Long.parseLong(begin.toString()), Long.parseLong(end.toString()), arr);
    }

    public String getPostCodeIntValue(String s, Map<String, String> zimuMap) {
        if (s.matches("[0-9]")) {
            return s;
        } else if ("*".equals(s)) {
            return "-1";
        } else {
            return zimuMap.get(s);
        }
    }


    @Override
    public ZoneTempImportRes importT(MultipartFile file) {
        List<ZoneTempImportVo> zoneTempImportVos = null;
        try {
            zoneTempImportVos = ExcelUtils.importExcel(file, 1, 0, ZoneTempImportVo.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        List<ZoneTempRuleAddReq> successTempRuleList = new ArrayList<>();
        List<ZoneTempErrorExportVo> errorExportVos = new ArrayList<>();
        List<ErrorInfo> errorList = new ArrayList<>();
        for (int i = 0; i < zoneTempImportVos.size(); i++) {
            ZoneTempImportVo importVo = zoneTempImportVos.get(i);
            if (StringUtils.isBlank(importVo.getZone()) || StringUtils.isBlank(importVo.getCountryCode())) continue;
            String msg = this.checkPostCode(importVo.getStartPostCode(), importVo.getEndPostCode());
            if (StringUtils.isNotBlank(msg)) {
                errorList.add(new ErrorInfo((i + 1) + "", StringsUtil.createI18Message(msg)));
                errorExportVos.add(new ZoneTempErrorExportVo(StringsUtil.createI18Message(msg), importVo.getZone(), importVo.getCountryCode(), importVo.getCity(), importVo.getStartPostCode(), importVo.getEndPostCode()));
            } else {
                successTempRuleList.add(new ZoneTempRuleAddReq(importVo.getZone(), importVo.getCountryCode(), importVo.getCity(), importVo.getStartPostCode(), importVo.getEndPostCode()));
            }
        }
        ZoneTempImportRes res = new ZoneTempImportRes();
        res.setFailNum(errorExportVos.size());
        res.setSuccessNum(successTempRuleList.size());
        res.setSuccessList(successTempRuleList);
        if (errorExportVos.isEmpty()) return res;
        String fileName = "错误_分区规则导入" + TimeUtils.getCurrentDateFormat("MMddHHmmss") + ".xlsx";

        try {
            ExcelUtils.exportExcelToSave(errorExportVos, ZoneTempErrorExportVo.class, fileName, new ExportParams(), filePathTemp);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String objectNameTemporary = "";
        if (filePath.startsWith("http")) {
            try {
                objectNameTemporary = AliOss.temporaryFile2(fileName, new File(filePathTemp + fileName));
            } catch (Exception e) {
                e.printStackTrace();
                log.error("导出文件失败" + e.getMessage());
            }
            res.setFailFileUrl(fileUrl + objectNameTemporary);
        } else {
            res.setFailFileUrl(fileUrl + fileName);
        }
        res.setFileName(fileName);
        res.setErrorList(errorList);
        return res;
    }

    @Override
    public void updateStatus(FreightStatusUpdateReq req, Long userId, Long tenantId) {
        ZoneTempDo et = dao.selectById(req.getId());
        if (et == null) return;
        if (et.getStatus() == req.getStatus()) return;
        et.setStatus(req.getStatus());
        dao.updateById(et);
//        operateRecordService.operateAddRecordAndValue(req.getStatus() == 0 ? "启动" : "停用", "", et.getId(), userId, null, OperateTypeEnum.Freight);
    }

}
